package emulator.hardware.io.peripherals.iec;

import java.nio.ByteBuffer;

import emulator.util.ByteBufferReader;

public class IecSimFsDriver implements IecSimDevice {
	private final static int MAX_CHANNELS = 16;
	private final static int CMD_CHANNEL = 15;
	private final static int NO_CHANNEL = -1;
	private FsDriverChannel channels[] = new FsDriverChannel [MAX_CHANNELS];
	private int active_channel = NO_CHANNEL;
	private IecSim iec_sim = null;
	private boolean is_in_open = false;
	private DriveStatus drive_status = new DriveStatus("IEC SIM FS DRIVER V0.1");
	private FsDirectory directory = new FsDirectory();
	private String dir_path = null;
	
	public IecSimFsDriver(String dir_path) {
		this.dir_path = dir_path;
	}

	public IecSimFsDriver() {
		this.dir_path = ".";
	}

	@Override
	public void setIecSim(IecSim iec_sim) {
		this.iec_sim = iec_sim;
		resetDevice();
		directory.load(dir_path);
	}
	
	private void resetDevice() {
		for (int i = 0; i < MAX_CHANNELS; i++)
			closeChannel(i);
		assignChannel(CMD_CHANNEL, new FsDriverCmdChannel(this), true);
		active_channel = NO_CHANNEL;
		drive_status.setStatus(DriveStatus.RESET, 0, 0);
	}

	private void closeChannel(int channel_no) {
		if (channels[channel_no] != null)
		{
			channels[channel_no].close();
			channels[channel_no] = null;
		}
		if (channel_no == active_channel)
			active_channel = NO_CHANNEL;
		if (channel_no == CMD_CHANNEL)
			assignChannel(channel_no, new FsDriverCmdChannel(this), false);
	}

	private void assignChannel(int channel_no, FsDriverChannel driver_channel, boolean do_open)
	{
		channels[channel_no] = driver_channel;
		if (do_open)
			channels[channel_no].open("");
	}

	@Override
	public void processCommand(ByteBuffer data) {
		while (data.hasRemaining())
		{
			int cmd = ByteBufferReader.getByte(data);
			switch (cmd & 0xF0)
			{
			case 0x60: // OPEN CHN/DATA
				openChannel(cmd & 0x0f);
				break;
			case 0xE0: // CLOSE
				close(cmd & 0x0f);
				break;
			case 0xF0: // OPEN
				open(cmd & 0x0f);
				break;
			}
		}
	}

	private void open(int channel) {
		switch (channel)
		{
		case CMD_CHANNEL:
			assignChannel(channel, new FsDriverCmdChannel(this), false);
			break;
		default:
			assignChannel(channel, new FsDriverFileChannel(this), false);
			break;
		}
		setActiveChannel(channel);
		is_in_open = true;
	}

	private void close(int channel) {
		closeChannel(channel);
	}

	private void openChannel(int channel) {
		setActiveChannel(channel);
		is_in_open = false;
	}

	public void talk() {
		iec_sim.initSend(this);
	}

	public void untalk() {
	}

	@Override
	public void processData(ByteBuffer data) {
		if (is_in_open)
		{
			getActiveChannel().open(ByteBufferReader.toString(data));
			is_in_open = false;
		}
		else
		{
			if (hasActiveChannel())
				getActiveChannel().write(data);
		}
	}

	@Override
	public void unlisten() {
		is_in_open = false;
	}

	@Override
	public void listen() {
	}

	public DriveStatus getDriveStatus() {
		return drive_status;
	}

	@Override
	public int read() {
		if (hasActiveChannel())
			return getActiveChannel().read();
		return -1;
	}

	private boolean hasActiveChannel() {
		return active_channel >= 0 &&
				channels[active_channel] != null;
	}

	private FsDriverChannel getActiveChannel() {
		return channels[active_channel];
	}

	private void setActiveChannel(int channel) {
		active_channel = channel;
	}

	public FsDirectory getDirectory() {
		return directory;
	}

	@Override
	public void rewind(int offset) {
		if (hasActiveChannel())
			getActiveChannel().rewind(offset);
	}
}
